<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen 1.9.2"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Agile Modbus: Agile Modbus</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="navtree.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="resize.js"></script>
<script type="text/javascript" src="navtreedata.js"></script>
<script type="text/javascript" src="navtree.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
 <tbody>
 <tr style="height: 56px;">
  <td id="projectalign" style="padding-left: 0.5em;">
   <div id="projectname">Agile Modbus<span id="projectnumber">&#160;1.1.2</span>
   </div>
   <div id="projectbrief">Lightweight modbus protocol stack.</div>
  </td>
 </tr>
 </tbody>
</table>
</div>
<!-- end header part -->
<!-- 制作者 Doxygen 1.9.2 -->
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&amp;dn=expat.txt MIT */
var searchBox = new SearchBox("searchBox", "search",'搜索','.html');
/* @license-end */
</script>
<script type="text/javascript" src="menudata.js"></script>
<script type="text/javascript" src="menu.js"></script>
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&amp;dn=expat.txt MIT */
$(function() {
  initMenu('',true,false,'search.php','搜索');
  $(document).ready(function() { init_search(); });
});
/* @license-end */
</script>
<div id="main-nav"></div>
</div><!-- top -->
<div id="side-nav" class="ui-resizable side-nav-resizable">
  <div id="nav-tree">
    <div id="nav-tree-contents">
      <div id="nav-sync" class="sync"></div>
    </div>
  </div>
  <div id="splitbar" style="-moz-user-select:none;" 
       class="ui-resizable-handle">
  </div>
</div>
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&amp;dn=expat.txt MIT */
$(document).ready(function(){initNavTree('index.html',''); initResizable(); });
/* @license-end */
</script>
<div id="doc-content">
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
     onmouseover="return searchBox.OnSearchSelectShow()"
     onmouseout="return searchBox.OnSearchSelectHide()"
     onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>

<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0" 
        name="MSearchResults" id="MSearchResults">
</iframe>
</div>

<div><div class="header">
  <div class="headertitle"><div class="title">Agile Modbus </div></div>
</div><!--header-->
<div class="contents">
<div class="textblock"><p ><a class="anchor" id="md_D__AgilePackages_agile_modbus_README"></a> </p>
<h1><a class="anchor" id="autotoc_md20"></a>
1、介绍</h1>
<p >Agile Modbus 即：轻量型 modbus 协议栈，满足用户任何场景下的使用需求。</p>
<p ><img src="./figures/ModbusProtocol.jpg" alt="ModbusProtocol" class="inline"/></p>
<ul>
<li>在线文档：<a href="https://loogg.github.io/AgileModbus_API/">API Manual</a></li>
<li><code>examples</code> 文件夹提供 PC 上的示例</li>
<li>MCU 上的示例查看 <a href="https://github.com/loogg/agile_modbus_mcu_demos">mcu_demos</a></li>
<li>在 AT32F437 上基于 RT-Thread 实现的支持 Modbus 固件升级的 Bootloader：<a href="https://github.com/loogg/AT32F437_Boot">AT32F437_Boot</a></li>
<li>在 HPM6750 上基于 RT-Thread 实现的支持 Modbus 固件升级的 Bootloader：<a href="https://github.com/loogg/HPM6750_Boot">HPM6750_Boot</a></li>
</ul>
<h2><a class="anchor" id="autotoc_md21"></a>
1.1、特性</h2>
<ol type="1">
<li>支持 rtu 及 tcp 协议，使用纯 C 开发，不涉及任何硬件接口，可在任何形式的硬件上直接使用。</li>
<li>由于其使用纯 C 开发、不涉及硬件，完全可以在串口上跑 tcp 协议，在网络上跑 rtu 协议。</li>
<li>支持符合 modbus 格式的自定义协议。</li>
<li>同时支持多主机和多从机。</li>
<li>使用简单，只需要将 rtu 或 tcp 句柄初始化好后，调用相应 API 进行组包和解包即可。</li>
</ol>
<h2><a class="anchor" id="autotoc_md22"></a>
1.2、目录结构</h2>
<table class="markdownTable">
<tr class="markdownTableHead">
<th class="markdownTableHeadNone">名称   </th><th class="markdownTableHeadNone">说明    </th></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone">doc   </td><td class="markdownTableBodyNone">文档    </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyNone">examples   </td><td class="markdownTableBodyNone">例子    </td></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone">figures   </td><td class="markdownTableBodyNone">素材    </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyNone">inc   </td><td class="markdownTableBodyNone">头文件    </td></tr>
<tr class="markdownTableRowOdd">
<td class="markdownTableBodyNone">src   </td><td class="markdownTableBodyNone">源代码    </td></tr>
<tr class="markdownTableRowEven">
<td class="markdownTableBodyNone">util   </td><td class="markdownTableBodyNone">提供简单实用的组件   </td></tr>
</table>
<h2><a class="anchor" id="autotoc_md23"></a>
1.3、许可证</h2>
<p >Agile Modbus 遵循 <code>Apache-2.0</code> 许可，详见 <code>LICENSE</code> 文件。</p>
<h1><a class="anchor" id="autotoc_md24"></a>
2、使用 Agile Modbus</h1>
<p >帮助文档请查看 <a href="./doc/doxygen/Agile_Modbus.chm">doc/doxygen/Agile_Modbus.chm</a></p>
<h2><a class="anchor" id="autotoc_md25"></a>
2.1、移植</h2>
<ul>
<li><p class="startli">用户需要实现硬件接口的 <code>发送数据</code> 、 <code>等待数据接收结束</code> 、 <code>清空接收缓存</code> 函数</p>
<p class="startli">对于 <code>等待数据接收结束</code>，提供如下几点思路：</p><ol type="1">
<li><p class="startli">通用方法</p>
<p class="startli">每隔 20 / 50 ms (该时间可根据波特率和硬件设置，这里只是给了参考值) 从硬件接口读取数据存放到缓冲区中并更新偏移，直到读取不到或缓冲区满，退出读取。</p>
<p class="startli">这对于裸机或操作系统都适用，操作系统可通过 <code>select</code> 或 <code>信号量</code> 方式完成阻塞。</p>
</li>
<li><p class="startli">串口 <code>DMA + IDLE</code> 中断方式</p>
<p class="startli">配置 <code>DMA + IDLE</code> 中断，在中断中使能标志，应用程序中判断该标志是否置位即可。</p>
<p class="startli">但该方案容易出问题，数据字节间稍微错开一点时间就不是一帧了。推荐第一种方案。</p>
</li>
</ol>
</li>
<li>主机：<ol type="1">
<li><code>agile_modbus_rtu_init</code> / <code>agile_modbus_tcp_init</code> 初始化 <code>RTU/TCP</code> 环境</li>
<li><code>agile_modbus_set_slave</code> 设置从机地址</li>
<li><code>清空接收缓存</code></li>
<li><code>agile_modbus_serialize_xxx</code> 打包请求数据</li>
<li><code>发送数据</code></li>
<li><code>等待数据接收结束</code></li>
<li><code>agile_modbus_deserialize_xxx</code> 解析响应数据</li>
<li>用户处理得到的数据</li>
</ol>
</li>
<li>从机：<ol type="1">
<li>实现 <code>agile_modbus_slave_callback_t</code> 类型回调函数</li>
<li><code>agile_modbus_rtu_init</code> / <code>agile_modbus_tcp_init</code> 初始化 <code>RTU/TCP</code> 环境</li>
<li><code>agile_modbus_set_slave</code> 设置从机地址</li>
<li><code>等待数据接收结束</code></li>
<li><code>agile_modbus_slave_handle</code> 处理请求数据</li>
<li><code>清空接收缓存</code> (可选)</li>
<li><code>发送数据</code></li>
</ol>
</li>
<li><p class="startli">特殊功能码</p>
<p class="startli">需要调用 <code>agile_modbus_set_compute_meta_length_after_function_cb</code> 和 <code>agile_modbus_set_compute_data_length_after_meta_cb</code> API 设置特殊功能码在主从模式下处理的回调。</p><ul>
<li><p class="startli"><code>agile_modbus_set_compute_meta_length_after_function_cb</code></p>
<p class="startli"><code>msg_type == AGILE_MODBUS_MSG_INDICATION</code>: 返回主机请求报文的数据元长度(uint8_t 类型)，不是特殊功能码必须返回 0。</p>
<p class="startli"><code>msg_type == MSG_CONFIRMATION</code>: 返回从机响应报文的数据元长度(uint8_t 类型)，不是特殊功能码必须返回 1。</p>
</li>
<li><p class="startli"><code>agile_modbus_set_compute_data_length_after_meta_cb</code></p>
<p class="startli"><code>msg_type == AGILE_MODBUS_MSG_INDICATION</code>: 返回主机请求报文数据元之后的数据长度，不是特殊功能码必须返回 0。</p>
<p class="startli"><code>msg_type == MSG_CONFIRMATION</code>: 返回从机响应报文数据元之后的数据长度，不是特殊功能码必须返回 0。</p>
</li>
</ul>
</li>
<li><p class="startli"><code>agile_modbus_rtu_init</code> / <code>agile_modbus_tcp_init</code></p>
<p class="startli">初始化 <code>RTU/TCP</code> 环境时需要用户传入 <code>发送缓冲区</code> 和 <code>接收缓冲区</code>，建议这两个缓冲区大小都为 <code>AGILE_MODBUS_MAX_ADU_LENGTH</code> (260) 字节。<code>特殊功能码</code> 情况用户根据协议自行决定。</p>
<p class="startli">但对于小内存 MCU，这两个缓冲区也可以设置小，所有 API 都会对缓冲区大小进行判断：</p>
<p class="startli">发送缓冲区设置：如果 <code>预期请求的数据长度</code> 或 <code>预期响应的数据长度</code> 大于 <code>设置的发送缓冲区大小</code>，返回异常。</p>
<p class="startli">接收缓冲区设置：如果 <code>主机请求的报文长度</code> 大于 <code>设置的接收缓冲区大小</code>，返回异常。这个是合理的，小内存 MCU 做从机肯定是需要对某些功能码做限制的。</p>
</li>
</ul>
<h2><a class="anchor" id="autotoc_md26"></a>
2.2、主机</h2>
<p >见 <code>2.1、移植</code>。</p>
<h2><a class="anchor" id="autotoc_md27"></a>
2.3、从机</h2>
<h3><a class="anchor" id="autotoc_md28"></a>
2.3.1、接口说明</h3>
<ul>
<li><p class="startli"><code>agile_modbus_slave_handle</code> 介绍</p>
<div class="fragment"><div class="line"><span class="keywordtype">int</span> <a class="code hl_function" href="group___slave___operation___functions.html#ga9ddc38e7ac14384c02b07b8927165247">agile_modbus_slave_handle</a>(<a class="code hl_struct" href="structagile__modbus.html">agile_modbus_t</a> *ctx, <span class="keywordtype">int</span> msg_length, uint8_t slave_strict,</div>
<div class="line">                              <a class="code hl_typedef" href="group___slave___exported___types.html#gae66e6077fe07d589d91121ac8874541a">agile_modbus_slave_callback_t</a> slave_cb, <span class="keyword">const</span> <span class="keywordtype">void</span> *slave_data, <span class="keywordtype">int</span> *frame_length)</div>
<div class="ttc" id="agroup___slave___exported___types_html_gae66e6077fe07d589d91121ac8874541a"><div class="ttname"><a href="group___slave___exported___types.html#gae66e6077fe07d589d91121ac8874541a">agile_modbus_slave_callback_t</a></div><div class="ttdeci">int(* agile_modbus_slave_callback_t)(agile_modbus_t *ctx, struct agile_modbus_slave_info *slave_info, const void *data)</div><div class="ttdoc">从机回调函数</div><div class="ttdef"><b>Definition:</b> <a href="agile__modbus_8h_source.html#l00251">agile_modbus.h:251</a></div></div>
<div class="ttc" id="agroup___slave___operation___functions_html_ga9ddc38e7ac14384c02b07b8927165247"><div class="ttname"><a href="group___slave___operation___functions.html#ga9ddc38e7ac14384c02b07b8927165247">agile_modbus_slave_handle</a></div><div class="ttdeci">int agile_modbus_slave_handle(agile_modbus_t *ctx, int msg_length, uint8_t slave_strict, agile_modbus_slave_callback_t slave_cb, const void *slave_data, int *frame_length)</div><div class="ttdoc">从机数据处理</div><div class="ttdef"><b>Definition:</b> <a href="agile__modbus_8c_source.html#l01203">agile_modbus.c:1203</a></div></div>
<div class="ttc" id="astructagile__modbus_html"><div class="ttname"><a href="structagile__modbus.html">agile_modbus</a></div><div class="ttdoc">Agile Modbus 结构体</div><div class="ttdef"><b>Definition:</b> <a href="agile__modbus_8h_source.html#l00203">agile_modbus.h:203</a></div></div>
</div><!-- fragment --><p class="startli">msg_length: <code>等待数据接收结束</code> 后接收到的数据长度。</p>
<p class="startli">slave_strict: 从机地址严格性检查 (0: 不判断地址是否一致，由用户回调处理; 1: 地址必须一致，否则不会调用回调，也不打包响应数据)。</p>
<p class="startli">slave_cb: <code>agile_modbus_slave_callback_t</code> 类型回调函数，用户实现并传入。如果为 NULL，所有功能码都能响应且为成功，但寄存器数据依然为 0。</p>
<p class="startli">slave_data: 从机回调函数私有数据。</p>
<p class="startli">frame_length: 获取解析出的 modbus 数据帧长度。这个参数的意义在于：</p><ol type="1">
<li>尾部有脏数据: 仍能解析成功，并告诉用户真实的 modbus 帧长，用户可以进行处理</li>
<li>数据粘包: 数据由 <code>一帧完整的 modbus 数据 + 部分 modbus 数据帧</code> 组成，用户获得真实 modbus 帧长后，可以移除处理完的 modbus 数据帧，再次读取硬件接口数据与当前 <code>部分 modbus 数据帧</code> 组成新的一帧</li>
<li>该参数在 modbus 广播传输大数据时使用较多(如：自定义功能码广播升级固件)，普通的从机响应都是一问一答式，只处理完整数据帧就行，建议在响应前执行 <code>清空接收缓存</code></li>
</ol>
</li>
<li><p class="startli"><code>agile_modbus_slave_callback_t</code> 介绍</p>
<div class="fragment"><div class="line"> </div>
<div class="line"><span class="keyword">typedef</span> int (*<a class="code hl_typedef" href="group___slave___exported___types.html#gae66e6077fe07d589d91121ac8874541a">agile_modbus_slave_callback_t</a>)(<a class="code hl_struct" href="structagile__modbus.html">agile_modbus_t</a> *ctx, <span class="keyword">struct </span><a class="code hl_struct" href="structagile__modbus__slave__info.html">agile_modbus_slave_info</a> *slave_info, <span class="keyword">const</span> <span class="keywordtype">void</span> *data);</div>
<div class="ttc" id="astructagile__modbus__slave__info_html"><div class="ttname"><a href="structagile__modbus__slave__info.html">agile_modbus_slave_info</a></div><div class="ttdoc">Agile Modbus 从机信息结构体</div><div class="ttdef"><b>Definition:</b> <a href="agile__modbus_8h_source.html#l00232">agile_modbus.h:232</a></div></div>
</div><!-- fragment --><p class="startli"><code><a class="el" href="structagile__modbus__slave__info.html" title="Agile Modbus 从机信息结构体">agile_modbus_slave_info</a></code>:</p>
<p class="startli">sft: 包含从机地址和功能码属性，回调中可利用</p>
<p class="startli">rsp_length: 响应数据长度指针，回调中处理 <code>特殊功能码</code> 时需要更新其值，否则 <b>不准更改</b></p>
<p class="startli">address: 寄存器地址 (不是所有功能码都用到)</p>
<p class="startli">nb: 数目 (不是所有功能码都用到)</p>
<p class="startli">buf: 不同功能码需要使用的数据域 (不是所有功能码都用到)</p>
<p class="startli">send_index: 发送缓冲区当前索引 (不是所有功能码都用到)</p>
</li>
<li><code><a class="el" href="structagile__modbus__slave__info.html" title="Agile Modbus 从机信息结构体">agile_modbus_slave_info</a></code> 不同功能码使用<ul>
<li><p class="startli">AGILE_MODBUS_FC_READ_COILS、AGILE_MODBUS_FC_READ_DISCRETE_INPUTS</p>
<p class="startli">需要使用到 <code>address</code>、<code>nb</code>、<code>send_index</code> 属性，需要调用 <code>agile_modbus_slave_io_set</code> API 将 IO 数据存放到 <code>ctx-&gt;send_buf + send_index</code> 开始的数据区域。</p>
</li>
<li><p class="startli">AGILE_MODBUS_FC_READ_HOLDING_REGISTERS、AGILE_MODBUS_FC_READ_INPUT_REGISTERS</p>
<p class="startli">需要使用到 <code>address</code>、<code>nb</code>、<code>send_index</code> 属性，需要调用 <code>agile_modbus_slave_register_set</code> API 将寄存器数据存放到 <code>ctx-&gt;send_buf + send_index</code> 开始的数据区域。</p>
</li>
<li><p class="startli">AGILE_MODBUS_FC_WRITE_SINGLE_COIL、AGILE_MODBUS_FC_WRITE_SINGLE_REGISTER</p>
<p class="startli">需要使用到 <code>address</code>、<code>buf</code> 属性，将 <code>buf</code> 强转为 <code>int *</code> 类型，获取值存放到寄存器中。</p>
</li>
<li><p class="startli">AGILE_MODBUS_FC_WRITE_MULTIPLE_COILS</p>
<p class="startli">需要使用到 <code>address</code>、<code>nb</code>、<code>buf</code> 属性，需要调用 <code>agile_modbus_slave_io_get</code> API 获取要写入的 IO 数据。</p>
</li>
<li><p class="startli">AGILE_MODBUS_FC_WRITE_MULTIPLE_REGISTERS</p>
<p class="startli">需要使用到 <code>address</code>、<code>nb</code>、<code>buf</code> 属性，需要调用 <code>agile_modbus_slave_register_get</code> API 获取要写入的寄存器数据。</p>
</li>
<li><p class="startli">AGILE_MODBUS_FC_MASK_WRITE_REGISTER</p>
<p class="startli">需要使用到 <code>address</code>、<code>buf</code> 属性，通过 <code>(buf[0] &lt;&lt; 8) + buf[1]</code> 获取 <code>and</code> 值，通过 <code>(buf[2] &lt;&lt; 8) + buf[3]</code> 获取 <code>or</code> 值。获取寄存器值 <code>data</code>，进行 <code>data = (data &amp; and) | (or &amp; (~and))</code> 操作更新 <code>data</code> 值，写入寄存器。</p>
</li>
<li><p class="startli">AGILE_MODBUS_FC_WRITE_AND_READ_REGISTERS</p>
<p class="startli">需要使用到 <code>address</code>、<code>buf</code>、<code>send_index</code> 属性，通过 <code>(buf[0] &lt;&lt; 8) + buf[1]</code> 获取要读取的寄存器数目，通过 <code>(buf[2] &lt;&lt; 8) + buf[3]</code> 获取要写入的寄存器地址，通过 <code>(buf[4] &lt;&lt; 8) + buf[5]</code> 获取要写入的寄存器数目。需要调用 <code>agile_modbus_slave_register_get</code> API 获取要写入的寄存器数据，调用 <code>agile_modbus_slave_register_set</code> API 将寄存器数据存放到 <code>ctx-&gt;send_buf + send_index</code> 开始的数据区域。</p>
</li>
<li><p class="startli">自定义功能码</p>
<p class="startli">需要使用到 <code>send_index</code>、<code>nb</code>、<code>buf</code> 属性，用户在回调中处理数据。</p>
<p class="startli">send_index: 发送缓冲区当前索引</p>
<p class="startli">nb: PUD - 1，也就是 modbus 数据域长度</p>
<p class="startli">buf: modbus 数据域起始位置</p>
<p class="startli"><b>注意</b>: 用户在回调中往发送缓冲区填入数据后，需要更新 <code><a class="el" href="structagile__modbus__slave__info.html" title="Agile Modbus 从机信息结构体">agile_modbus_slave_info</a></code> 的 <code>rsp_length</code> 值。</p>
</li>
</ul>
</li>
</ul>
<h3><a class="anchor" id="autotoc_md29"></a>
2.3.2、简易从机接入接口</h3>
<p >Agile Modbus 提供了 <code>agile_modbus_slave_callback_t</code> 的一种实现方式，使用户能够简单方便接入。</p>
<p >使用示例可查看 <a href="./examples/slave">examples/slave</a>。</p>
<p >使用方式：</p>
<div class="fragment"><div class="line"><span class="preprocessor">#include &quot;<a class="code" href="agile__modbus_8h.html">agile_modbus.h</a>&quot;</span></div>
<div class="line"><span class="preprocessor">#include &quot;<a class="code" href="agile__modbus__slave__util_8h.html">agile_modbus_slave_util.h</a>&quot;</span></div>
<div class="line"> </div>
<div class="line"><span class="keyword">const</span> <a class="code hl_struct" href="structagile__modbus__slave__util.html">agile_modbus_slave_util_t</a> slave_util = {</div>
<div class="line">  <span class="comment">/* User implementation */</span></div>
<div class="line"> </div>
<div class="line">};</div>
<div class="line"> </div>
<div class="line"><a class="code hl_function" href="group___slave___operation___functions.html#ga9ddc38e7ac14384c02b07b8927165247">agile_modbus_slave_handle</a>(ctx, read_len, 0, <a class="code hl_function" href="group___s_l_a_v_e___u_t_i_l___exported___functions.html#ga6d7613fbda26c40a969df673cf9b8c5b">agile_modbus_slave_util_callback</a>, &amp;slave_util, NULL);</div>
<div class="ttc" id="aagile__modbus_8h_html"><div class="ttname"><a href="agile__modbus_8h.html">agile_modbus.h</a></div><div class="ttdoc">Agile Modbus 软件包通用头文件</div></div>
<div class="ttc" id="aagile__modbus__slave__util_8h_html"><div class="ttname"><a href="agile__modbus__slave__util_8h.html">agile_modbus_slave_util.h</a></div><div class="ttdoc">Agile Modbus 软件包提供的简易从机接入头文件</div></div>
<div class="ttc" id="agroup___s_l_a_v_e___u_t_i_l___exported___functions_html_ga6d7613fbda26c40a969df673cf9b8c5b"><div class="ttname"><a href="group___s_l_a_v_e___u_t_i_l___exported___functions.html#ga6d7613fbda26c40a969df673cf9b8c5b">agile_modbus_slave_util_callback</a></div><div class="ttdeci">int agile_modbus_slave_util_callback(agile_modbus_t *ctx, struct agile_modbus_slave_info *slave_info, const void *data)</div><div class="ttdoc">从机回调函数</div><div class="ttdef"><b>Definition:</b> <a href="agile__modbus__slave__util_8c_source.html#l00387">agile_modbus_slave_util.c:387</a></div></div>
<div class="ttc" id="astructagile__modbus__slave__util_html"><div class="ttname"><a href="structagile__modbus__slave__util.html">agile_modbus_slave_util</a></div><div class="ttdoc">从机功能结构体</div><div class="ttdef"><b>Definition:</b> <a href="agile__modbus__slave__util_8h_source.html#l00048">agile_modbus_slave_util.h:48</a></div></div>
</div><!-- fragment --><ul>
<li><code>agile_modbus_slave_util_callback</code> 介绍<ul>
<li>Agile Modbus 提供的一种 <code>agile_modbus_slave_callback_t</code> 实现方式，需要 <code>agile_modbus_slave_util_t</code> 类型变量指针作为私有数据。</li>
<li>私有数据为 NULL，所有功能码都能响应且为成功，但寄存器数据依然为 0。</li>
</ul>
</li>
<li><p class="startli"><code>agile_modbus_slave_util_t</code> 介绍</p>
<div class="fragment"><div class="line"><span class="keyword">typedef</span> <span class="keyword">struct </span><a class="code hl_struct" href="structagile__modbus__slave__util.html">agile_modbus_slave_util</a> {</div>
<div class="line">    <span class="keyword">const</span> <a class="code hl_struct" href="structagile__modbus__slave__util__map.html">agile_modbus_slave_util_map_t</a> *<a class="code hl_variable" href="structagile__modbus__slave__util.html#adf60d2aacae135df7fb77f1fd567a0f3">tab_bits</a>;                                            </div>
<div class="line">    <span class="keywordtype">int</span> <a class="code hl_variable" href="structagile__modbus__slave__util.html#a54d8cd5ea2ece03fc4748f857d433470">nb_bits</a>;                                                                              </div>
<div class="line">    <span class="keyword">const</span> <a class="code hl_struct" href="structagile__modbus__slave__util__map.html">agile_modbus_slave_util_map_t</a> *<a class="code hl_variable" href="structagile__modbus__slave__util.html#ab8dd5a6fa6cb534aa90460cf9a71ae6f">tab_input_bits</a>;                                      </div>
<div class="line">    <span class="keywordtype">int</span> <a class="code hl_variable" href="structagile__modbus__slave__util.html#a81fb44a47ea6e8dc7eb190b87ddb6dc4">nb_input_bits</a>;                                                                        </div>
<div class="line">    <span class="keyword">const</span> <a class="code hl_struct" href="structagile__modbus__slave__util__map.html">agile_modbus_slave_util_map_t</a> *<a class="code hl_variable" href="structagile__modbus__slave__util.html#ae21eece6d89b5d46839496e9281108f8">tab_registers</a>;                                       </div>
<div class="line">    <span class="keywordtype">int</span> <a class="code hl_variable" href="structagile__modbus__slave__util.html#ac7c7bd2cc1687ae7c291a054d3071114">nb_registers</a>;                                                                         </div>
<div class="line">    <span class="keyword">const</span> <a class="code hl_struct" href="structagile__modbus__slave__util__map.html">agile_modbus_slave_util_map_t</a> *<a class="code hl_variable" href="structagile__modbus__slave__util.html#a67c8df610dcb3f8dee4d32ed920f4886">tab_input_registers</a>;                                 </div>
<div class="line">    <span class="keywordtype">int</span> <a class="code hl_variable" href="structagile__modbus__slave__util.html#a3c2c64bb74a0cab7f34f303fa3a37b31">nb_input_registers</a>;                                                                   </div>
<div class="line">    int (*<a class="code hl_variable" href="structagile__modbus__slave__util.html#a134270ba34165c677e85490b2bb6719c">addr_check</a>)(<a class="code hl_struct" href="structagile__modbus.html">agile_modbus_t</a> *ctx, <span class="keyword">struct </span><a class="code hl_struct" href="structagile__modbus__slave__info.html">agile_modbus_slave_info</a> *slave_info);       </div>
<div class="line">    int (*<a class="code hl_variable" href="structagile__modbus__slave__util.html#a70de0b2f19492b188c99ba332e2b2ffa">special_function</a>)(<a class="code hl_struct" href="structagile__modbus.html">agile_modbus_t</a> *ctx, <span class="keyword">struct </span><a class="code hl_struct" href="structagile__modbus__slave__info.html">agile_modbus_slave_info</a> *slave_info); </div>
<div class="line">    int (*<a class="code hl_variable" href="structagile__modbus__slave__util.html#abc6323db9d3873c30bee0e7efe96f4b8">done</a>)(<a class="code hl_struct" href="structagile__modbus.html">agile_modbus_t</a> *ctx, <span class="keyword">struct </span><a class="code hl_struct" href="structagile__modbus__slave__info.html">agile_modbus_slave_info</a> *slave_info, <span class="keywordtype">int</span> ret);    </div>
<div class="line">} <a class="code hl_typedef" href="group___s_l_a_v_e___u_t_i_l___exported___types.html#ga48cf733bea0cf7ad09b52b139348de50">agile_modbus_slave_util_t</a>;</div>
<div class="ttc" id="agroup___s_l_a_v_e___u_t_i_l___exported___types_html_ga48cf733bea0cf7ad09b52b139348de50"><div class="ttname"><a href="group___s_l_a_v_e___u_t_i_l___exported___types.html#ga48cf733bea0cf7ad09b52b139348de50">agile_modbus_slave_util_t</a></div><div class="ttdeci">struct agile_modbus_slave_util agile_modbus_slave_util_t</div><div class="ttdoc">从机功能结构体</div></div>
<div class="ttc" id="astructagile__modbus__slave__util__map_html"><div class="ttname"><a href="structagile__modbus__slave__util__map.html">agile_modbus_slave_util_map</a></div><div class="ttdoc">从机寄存器映射结构体</div><div class="ttdef"><b>Definition:</b> <a href="agile__modbus__slave__util_8h_source.html#l00038">agile_modbus_slave_util.h:38</a></div></div>
<div class="ttc" id="astructagile__modbus__slave__util_html_a134270ba34165c677e85490b2bb6719c"><div class="ttname"><a href="structagile__modbus__slave__util.html#a134270ba34165c677e85490b2bb6719c">agile_modbus_slave_util::addr_check</a></div><div class="ttdeci">int(* addr_check)(agile_modbus_t *ctx, struct agile_modbus_slave_info *slave_info)</div><div class="ttdoc">地址检查接口</div><div class="ttdef"><b>Definition:</b> <a href="agile__modbus__slave__util_8h_source.html#l00057">agile_modbus_slave_util.h:57</a></div></div>
<div class="ttc" id="astructagile__modbus__slave__util_html_a3c2c64bb74a0cab7f34f303fa3a37b31"><div class="ttname"><a href="structagile__modbus__slave__util.html#a3c2c64bb74a0cab7f34f303fa3a37b31">agile_modbus_slave_util::nb_input_registers</a></div><div class="ttdeci">int nb_input_registers</div><div class="ttdoc">输入寄存器定义数组数目</div><div class="ttdef"><b>Definition:</b> <a href="agile__modbus__slave__util_8h_source.html#l00056">agile_modbus_slave_util.h:56</a></div></div>
<div class="ttc" id="astructagile__modbus__slave__util_html_a54d8cd5ea2ece03fc4748f857d433470"><div class="ttname"><a href="structagile__modbus__slave__util.html#a54d8cd5ea2ece03fc4748f857d433470">agile_modbus_slave_util::nb_bits</a></div><div class="ttdeci">int nb_bits</div><div class="ttdoc">线圈寄存器定义数组数目</div><div class="ttdef"><b>Definition:</b> <a href="agile__modbus__slave__util_8h_source.html#l00050">agile_modbus_slave_util.h:50</a></div></div>
<div class="ttc" id="astructagile__modbus__slave__util_html_a67c8df610dcb3f8dee4d32ed920f4886"><div class="ttname"><a href="structagile__modbus__slave__util.html#a67c8df610dcb3f8dee4d32ed920f4886">agile_modbus_slave_util::tab_input_registers</a></div><div class="ttdeci">const agile_modbus_slave_util_map_t * tab_input_registers</div><div class="ttdoc">输入寄存器定义数组</div><div class="ttdef"><b>Definition:</b> <a href="agile__modbus__slave__util_8h_source.html#l00055">agile_modbus_slave_util.h:55</a></div></div>
<div class="ttc" id="astructagile__modbus__slave__util_html_a70de0b2f19492b188c99ba332e2b2ffa"><div class="ttname"><a href="structagile__modbus__slave__util.html#a70de0b2f19492b188c99ba332e2b2ffa">agile_modbus_slave_util::special_function</a></div><div class="ttdeci">int(* special_function)(agile_modbus_t *ctx, struct agile_modbus_slave_info *slave_info)</div><div class="ttdoc">特殊功能码处理接口</div><div class="ttdef"><b>Definition:</b> <a href="agile__modbus__slave__util_8h_source.html#l00058">agile_modbus_slave_util.h:58</a></div></div>
<div class="ttc" id="astructagile__modbus__slave__util_html_a81fb44a47ea6e8dc7eb190b87ddb6dc4"><div class="ttname"><a href="structagile__modbus__slave__util.html#a81fb44a47ea6e8dc7eb190b87ddb6dc4">agile_modbus_slave_util::nb_input_bits</a></div><div class="ttdeci">int nb_input_bits</div><div class="ttdoc">离散量输入寄存器定义数组数目</div><div class="ttdef"><b>Definition:</b> <a href="agile__modbus__slave__util_8h_source.html#l00052">agile_modbus_slave_util.h:52</a></div></div>
<div class="ttc" id="astructagile__modbus__slave__util_html_ab8dd5a6fa6cb534aa90460cf9a71ae6f"><div class="ttname"><a href="structagile__modbus__slave__util.html#ab8dd5a6fa6cb534aa90460cf9a71ae6f">agile_modbus_slave_util::tab_input_bits</a></div><div class="ttdeci">const agile_modbus_slave_util_map_t * tab_input_bits</div><div class="ttdoc">离散量输入寄存器定义数组</div><div class="ttdef"><b>Definition:</b> <a href="agile__modbus__slave__util_8h_source.html#l00051">agile_modbus_slave_util.h:51</a></div></div>
<div class="ttc" id="astructagile__modbus__slave__util_html_abc6323db9d3873c30bee0e7efe96f4b8"><div class="ttname"><a href="structagile__modbus__slave__util.html#abc6323db9d3873c30bee0e7efe96f4b8">agile_modbus_slave_util::done</a></div><div class="ttdeci">int(* done)(agile_modbus_t *ctx, struct agile_modbus_slave_info *slave_info, int ret)</div><div class="ttdoc">处理结束接口</div><div class="ttdef"><b>Definition:</b> <a href="agile__modbus__slave__util_8h_source.html#l00059">agile_modbus_slave_util.h:59</a></div></div>
<div class="ttc" id="astructagile__modbus__slave__util_html_ac7c7bd2cc1687ae7c291a054d3071114"><div class="ttname"><a href="structagile__modbus__slave__util.html#ac7c7bd2cc1687ae7c291a054d3071114">agile_modbus_slave_util::nb_registers</a></div><div class="ttdeci">int nb_registers</div><div class="ttdoc">保持寄存器定义数组数目</div><div class="ttdef"><b>Definition:</b> <a href="agile__modbus__slave__util_8h_source.html#l00054">agile_modbus_slave_util.h:54</a></div></div>
<div class="ttc" id="astructagile__modbus__slave__util_html_adf60d2aacae135df7fb77f1fd567a0f3"><div class="ttname"><a href="structagile__modbus__slave__util.html#adf60d2aacae135df7fb77f1fd567a0f3">agile_modbus_slave_util::tab_bits</a></div><div class="ttdeci">const agile_modbus_slave_util_map_t * tab_bits</div><div class="ttdoc">线圈寄存器定义数组</div><div class="ttdef"><b>Definition:</b> <a href="agile__modbus__slave__util_8h_source.html#l00049">agile_modbus_slave_util.h:49</a></div></div>
<div class="ttc" id="astructagile__modbus__slave__util_html_ae21eece6d89b5d46839496e9281108f8"><div class="ttname"><a href="structagile__modbus__slave__util.html#ae21eece6d89b5d46839496e9281108f8">agile_modbus_slave_util::tab_registers</a></div><div class="ttdeci">const agile_modbus_slave_util_map_t * tab_registers</div><div class="ttdoc">保持寄存器定义数组</div><div class="ttdef"><b>Definition:</b> <a href="agile__modbus__slave__util_8h_source.html#l00053">agile_modbus_slave_util.h:53</a></div></div>
</div><!-- fragment --><ul>
<li><p class="startli">寄存器相关</p>
<p class="startli">用户需要实现 <code>bits</code>、<code>input_bits</code>、<code>registers</code>、<code>input_registers</code> 定义。如果某个寄存器定义为 NULL，该寄存器对应的功能码能响应且为成功，但寄存器数据都为 0。</p>
</li>
<li><p class="startli">接口调用过程</p>
<p class="startli"><img src="./figures/SlaveCallback.jpg" alt="SlaveCallback" class="inline"/></p>
</li>
</ul>
</li>
<li><p class="startli"><code><a class="el" href="structagile__modbus__slave__util__map.html" title="从机寄存器映射结构体">agile_modbus_slave_util_map</a></code> 介绍</p>
<div class="fragment"><div class="line"><span class="keyword">typedef</span> <span class="keyword">struct </span><a class="code hl_struct" href="structagile__modbus__slave__util__map.html">agile_modbus_slave_util_map</a> {</div>
<div class="line">    <span class="keywordtype">int</span> <a class="code hl_variable" href="structagile__modbus__slave__util__map.html#a9b130b4a875dca0d63c31d81fbc8b0f1">start_addr</a>;                                       </div>
<div class="line">    <span class="keywordtype">int</span> <a class="code hl_variable" href="structagile__modbus__slave__util__map.html#ace624a13ffd6380963e9173d6f8f4eb9">end_addr</a>;                                         </div>
<div class="line">    int (*<a class="code hl_variable" href="structagile__modbus__slave__util__map.html#a7fd43329330824f98bdd5da3e8b8f170">get</a>)(<span class="keywordtype">void</span> *buf, <span class="keywordtype">int</span> bufsz);                     </div>
<div class="line">    int (*<a class="code hl_variable" href="structagile__modbus__slave__util__map.html#aeb703aff86edfb371edb56e0122d51ab">set</a>)(<span class="keywordtype">int</span> index, <span class="keywordtype">int</span> len, <span class="keywordtype">void</span> *buf, <span class="keywordtype">int</span> bufsz); </div>
<div class="line">} <a class="code hl_typedef" href="group___s_l_a_v_e___u_t_i_l___exported___types.html#ga704dafcb7fb13e228bfbf8744dd5a3f8">agile_modbus_slave_util_map_t</a>;</div>
<div class="ttc" id="agroup___s_l_a_v_e___u_t_i_l___exported___types_html_ga704dafcb7fb13e228bfbf8744dd5a3f8"><div class="ttname"><a href="group___s_l_a_v_e___u_t_i_l___exported___types.html#ga704dafcb7fb13e228bfbf8744dd5a3f8">agile_modbus_slave_util_map_t</a></div><div class="ttdeci">struct agile_modbus_slave_util_map agile_modbus_slave_util_map_t</div><div class="ttdoc">从机寄存器映射结构体</div></div>
<div class="ttc" id="astructagile__modbus__slave__util__map_html_a7fd43329330824f98bdd5da3e8b8f170"><div class="ttname"><a href="structagile__modbus__slave__util__map.html#a7fd43329330824f98bdd5da3e8b8f170">agile_modbus_slave_util_map::get</a></div><div class="ttdeci">int(* get)(void *buf, int bufsz)</div><div class="ttdoc">获取寄存器数据接口</div><div class="ttdef"><b>Definition:</b> <a href="agile__modbus__slave__util_8h_source.html#l00041">agile_modbus_slave_util.h:41</a></div></div>
<div class="ttc" id="astructagile__modbus__slave__util__map_html_a9b130b4a875dca0d63c31d81fbc8b0f1"><div class="ttname"><a href="structagile__modbus__slave__util__map.html#a9b130b4a875dca0d63c31d81fbc8b0f1">agile_modbus_slave_util_map::start_addr</a></div><div class="ttdeci">int start_addr</div><div class="ttdoc">起始地址</div><div class="ttdef"><b>Definition:</b> <a href="agile__modbus__slave__util_8h_source.html#l00039">agile_modbus_slave_util.h:39</a></div></div>
<div class="ttc" id="astructagile__modbus__slave__util__map_html_ace624a13ffd6380963e9173d6f8f4eb9"><div class="ttname"><a href="structagile__modbus__slave__util__map.html#ace624a13ffd6380963e9173d6f8f4eb9">agile_modbus_slave_util_map::end_addr</a></div><div class="ttdeci">int end_addr</div><div class="ttdoc">结束地址</div><div class="ttdef"><b>Definition:</b> <a href="agile__modbus__slave__util_8h_source.html#l00040">agile_modbus_slave_util.h:40</a></div></div>
<div class="ttc" id="astructagile__modbus__slave__util__map_html_aeb703aff86edfb371edb56e0122d51ab"><div class="ttname"><a href="structagile__modbus__slave__util__map.html#aeb703aff86edfb371edb56e0122d51ab">agile_modbus_slave_util_map::set</a></div><div class="ttdeci">int(* set)(int index, int len, void *buf, int bufsz)</div><div class="ttdoc">设置寄存器数据接口</div><div class="ttdef"><b>Definition:</b> <a href="agile__modbus__slave__util_8h_source.html#l00042">agile_modbus_slave_util.h:42</a></div></div>
</div><!-- fragment --><ul>
<li><b>注意事项</b>:<ul>
<li>起始地址和结束地址决定的寄存器个数有限制。更改函数内部 <code>map_buf</code> 数组大小可使其变大。<ul>
<li>bit 寄存器 &lt; 250</li>
<li>register 寄存器 &lt; 125</li>
</ul>
</li>
<li>接口函数为 NULL，寄存器对应的功能码能响应且为成功。</li>
</ul>
</li>
<li><p class="startli"><code>get</code> 接口</p>
<p class="startli">将地址域内的数据全部拷贝到 <code>buf</code> 中。</p>
</li>
<li><p class="startli"><code>set</code> 接口</p><ul>
<li><code>index</code>: 地址域内的偏移</li>
<li><code>len</code>: 长度</li>
</ul>
<p class="startli">根据 <code>index</code> 和 <code>len</code> 修改数据。</p>
</li>
</ul>
</li>
</ul>
<h2><a class="anchor" id="autotoc_md30"></a>
2.4、示例</h2>
<ul>
<li><a href="./examples">examples</a> 文件夹中提供 PC 上的示例，可以在 <code>WSL</code> 或 <code>Linux</code> 下编译运行。<ul>
<li>RTU / TCP 主机、从机的示例</li>
<li><p class="startli">特殊功能码的示例</p>
<p class="startli">RTU 点对点传输文件: 演示特殊功能码的使用方式</p>
<p class="startli">RTU 广播传输文件: 演示 <code>agile_modbus_slave_handle</code> 中 <code>frame_length</code> 的用处</p>
</li>
</ul>
</li>
<li><a href="https://github.com/loogg/agile_modbus_mcu_demos">mcu_demos</a> 提供在 MCU 上的例子。</li>
<li><a href="https://github.com/loogg/AT32F437_Boot">AT32F437_Boot</a> 在 AT32F437 上基于 RT-Thread 实现的支持 Modbus 固件升级的 Bootloader。</li>
<li><a href="https://github.com/loogg/HPM6750_Boot">HPM6750_Boot</a> 在 HPM6750 上基于 RT-Thread 实现的支持 Modbus 固件升级的 Bootloader。</li>
</ul>
<h2><a class="anchor" id="autotoc_md31"></a>
2.5、Doxygen 文档生成</h2>
<ul>
<li>使用 <code>Doxywizard</code> 打开 <a href="./doc/doxygen/Doxyfile">Doxyfile</a> 运行，生成的文件在 <a href="./doc/doxygen/output">doxygen/output</a> 下。</li>
<li>需要更改 <code>Graphviz</code> 路径。</li>
<li><code>HTML</code> 生成未使用 <code>chm</code> 格式的，如果使能需要更改 <code>hhc.exe</code> 路径。</li>
</ul>
<h1><a class="anchor" id="autotoc_md32"></a>
3、支持</h1>
<p ><img src="./figures/zanshang.jpg" alt="zanshang" class="inline"/></p>
<p >如果 Agile Modbus 解决了你的问题，不妨扫描上面二维码请我 <b>喝杯咖啡</b> ~</p>
<h1><a class="anchor" id="autotoc_md33"></a>
4、联系方式 &amp; 感谢</h1>
<ul>
<li>维护：马龙伟</li>
<li>主页：<a href="https://github.com/loogg/agile_modbus">https://github.com/loogg/agile_modbus</a></li>
<li>邮箱：<a href="#" onclick="location.href='mai'+'lto:'+'254'+'40'+'472'+'13'+'@qq'+'.c'+'om'; return false;">25440<span style="display: none;">.nosp@m.</span>4721<span style="display: none;">.nosp@m.</span>3@qq.<span style="display: none;">.nosp@m.</span>com</a> </li>
</ul>
</div></div><!-- PageDoc -->
</div><!-- contents -->
</div><!-- doc-content -->
<!-- start footer part -->
<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
  <ul>
    <li class="footer">制作者 <a href="https://www.doxygen.org/index.html"><img class="footer" src="doxygen.svg" width="104" height="31" alt="doxygen"/></a> 1.9.2 </li>
  </ul>
</div>
</body>
</html>
